// VInst.cpp
// implement the VInst class

#include "crossbit/VInst.h"
#include <iostream>
#include <string>
#include <sstream>

using namespace std;
namespace crossbit {

    ostream&
        operator<<(ostream& os, const VInst& inst)
        {
            Opcode op = inst.getOpcode();
            DataType type = inst.getDataType();
            DataType type2 = inst.getDataType2();
            string format;

#define VINSTTEMPLATE(OPCODE, FORMATSTRING) \
            case OPCODE: format = FORMATSTRING; break;

                         switch(op) {

                             // VInstruction Template Definition:
                             // VINSTTEMPLATE(OPCODE, <format-string>), 
                             //   where <format-string> specifies how to print VInst to text-form
                             //   %r: register
                             //   %i: immediate
                             //   %d: data address
                             //   %p: program counter
                             //   %b: block size
                             //   %c: compare condition code
                             //   %w: plain word in hex
                             //   %t: data type
                             //   %g: tag
                             //
                             VINSTTEMPLATE(OP_GET,	    "GET%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_PUT,	    "PUT%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_LD,	    "LD%t \t\t%r(%i), %r")
                                 VINSTTEMPLATE(OP_ST,	    "ST%t \t\t%r, %r(%i)")
                                 VINSTTEMPLATE(OP_MOV,	    "MOV%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_MOV_T_R,	    "MOV%t \t\t%g, %r")
                                 VINSTTEMPLATE(OP_MOV_R_T,	    "MOV%t \t\t%r, %g")
                                 VINSTTEMPLATE(OP_LI,	    "LI%t \t\t%i, %r")
                                 VINSTTEMPLATE(OP_ADD,	    "ADD%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_SUB,	    "SUB%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_ADDI,	    "ADDI%t \t\t%r, %i, %r")
                                 VINSTTEMPLATE(OP_SUBI,	    "SUBI%t \t\t%r, %i, %r")
                                 VINSTTEMPLATE(OP_AND,	    "AND%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_OR,	    "OR%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_XOR,	    "XOR%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_NOT,	    "NOT%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_MUL,	    "MUL%t \t\t%r, %r, %r, %r, %r")
                                 //VINSTTEMPLATE(OP_MULU,	    "MULU.%t \t\tv%r, v%r, v%r, v%r")
                                 VINSTTEMPLATE(OP_DIV,	    "DIV%t \t\t%r, %r, %r, %r, %r")
                                 //VINSTTEMPLATE(OP_DIVU,	    "DIVU.%t \t\tv%r, v%r, v%r, v%r")
                                 VINSTTEMPLATE(OP_SLL,	    "SLL%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_SLLV,	    "SLLV%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_SRL,	    "SRL%t \t\t%r, %i, %r")
                                 VINSTTEMPLATE(OP_SRLV,	    "SRLV%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_SRA,	    "SRA%t \t\t%r, %i, %r")
                                 VINSTTEMPLATE(OP_SRAV,	    "SRAV%t \t\t%r, %r, %r")
                                 VINSTTEMPLATE(OP_SEXT,	    "SEXT%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_ZEXT,	    "ZEXT%t \t\t%r, %r")
                                 VINSTTEMPLATE(OP_CMP,	    "CMP%t \t\t%c, %r, %r, %r")
                                 VINSTTEMPLATE(OP_JMP,	    "JMP \t\t%r(%i)")
                                 VINSTTEMPLATE(OP_BRANCH,    "BRANCH \t\t%c, %r, %r, %r(%i)")
                                 VINSTTEMPLATE(OP_HALT,	    "HALT")
                                 VINSTTEMPLATE(OP_SYSCALL,   "SYSCALL")
                                 VINSTTEMPLATE(OP_CALL,	    "CALL \t\t%w")
                                 // float-point
                                 VINSTTEMPLATE(OP_FGET_S,    "FGETS  %r")
                                 VINSTTEMPLATE(OP_FPUT_S,    "FPUTS  %r")
                                 VINSTTEMPLATE(OP_FGET_D,    "FGETD \t\t%r")
                                 VINSTTEMPLATE(OP_FPUT_D,    "FPUTD \t\t%r")
                                 VINSTTEMPLATE(OP_FLD,	    "FLD   %r(%i), %b, %r")
                                 VINSTTEMPLATE(OP_FST,	    "FST   %r, %b, %r(%i)")
                                 VINSTTEMPLATE(OP_FMOV,	    "FMOV  %r, %r")
								 VINSTTEMPLATE(OP_FCOS,		"FCOS	%r")
								 VINSTTEMPLATE(OP_FPTAN,	"FPTAN	%r")
								 VINSTTEMPLATE(OP_FSIN,		"FSIN	%r")
                                 VINSTTEMPLATE(OP_FADD_S,    "FADDS")
                                 VINSTTEMPLATE(OP_FADD_D,    "FADDD")
                                 VINSTTEMPLATE(OP_FSUB_S,    "FSUBS")
                                 VINSTTEMPLATE(OP_FSUB_D,    "FSUBD")
                                 VINSTTEMPLATE(OP_FMUL_S,    "FMULS")
                                 VINSTTEMPLATE(OP_FMUL_D,    "FMULD")
                                 VINSTTEMPLATE(OP_FDIV_S,    "FDIVS")
                                 VINSTTEMPLATE(OP_FDIV_D,    "FDIVD")
                                 VINSTTEMPLATE(OP_FNEG_S,    "FNEGS")
                                 VINSTTEMPLATE(OP_FNEG_D,    "FNEGD")
                                 VINSTTEMPLATE(OP_FABS_S,    "FABSS")
                                 VINSTTEMPLATE(OP_FABS_D,    "FABSD")
                                 VINSTTEMPLATE(OP_FSQRT_S,   "FSQRTS")
                                 VINSTTEMPLATE(OP_FSQRT_D,   "FSQRTD")
                                 VINSTTEMPLATE(OP_FCMP_S,    "FCMPS")
                                 VINSTTEMPLATE(OP_FCMP_D,    "FCMPD")
                                 VINSTTEMPLATE(OP_CONV,	 "CONV%t%y \t%r, %r")
                         }

                         string::size_type idx = 0;	// string index
                         int nopr = 0;		// operand index
                         while ( (idx = format.find('%', idx)) != string::npos) {
                             char f = format[idx + 1];
                             Operand opr;
                             if(f != 't' && f != 'y') opr = inst.getOperand(++nopr);
                             stringstream ss;

                             switch (f) {
								 case 'g':	// Tag
									 switch(opr.tag)
									 {
										 case TidX:
											 ss << "TidX";
											 break;
										 case TidY:
											 ss << "TidY";
											 break;
										 case TidZ:
											 ss << "TidZ";
											 break;
										 case TidA:
											 ss << "TidA";
											 break;
										 case MatrixA:
											 ss << "MatrixA";
											 break;
										 case MatrixB:
											 ss << "MatrixB";
											 break;
										 case MatrixC:
											 ss << "MatrixC";
											 break;
										 case WA:
											 ss << "WA";
											 break;
										 case HA:
											 ss << "HA";
											 break;
										 case WB:
											 ss << "WB";
											 break;
										 case HB:
											 ss << "HB";
											 break;
										 case TEMP0:
											 ss << "TEMP0";
											 break;
										 case TEMP1:
											 ss << "TEMP1";
											 break;
										 case TEMP2:
											 ss << "TEMP2";
											 break;
										 case TEMP3:
											 ss << "TEMP3";
											 break;
										 case TEMP4:
											 ss << "TEMP4";
											 break;
										 case TEMP5:
											 ss << "TEMP5";
											 break;
										 case TEMP6:
											 ss << "TEMP6";
											 break;
										 case TEMP7:
											 ss << "TEMP7";
											 break;
										 case TEMP8:
											 ss << "TEMP8";
											 break;
										 case TEMP9:
											 ss << "TEMP9";
											 break;
										 case ARG0:
											 ss << "ARG0";
											 break;
										 case ARG1:
											 ss << "ARG1";
											 break;
										 case ARG2:
											 ss << "ARG2";
											 break;
										 default:
											 printf("tag = %d\n", opr.tag);
											 assert(0 && "tag not supported!");
									 }
									 break;
                                 case 'r':	// register
                                     if(-1 == opr.reg) ss << "NA";    // this should only happen in MUL & DIV
                                     else{
                                         switch(opr.type)
                                         {
                                             case Operand::SREG_USE:
                                             case Operand::SREG_DEF:
                                                 ss << "s" << opr.reg;
                                                 break;
                                             case Operand::VREG_USE:
                                             case Operand::VREG_DEF:
                                                 ss << "v" << opr.reg;
                                                 break;
                                             case Operand::FREG_USE:
                                             case Operand::FREG_DEF:
                                                 ss << "f" << opr.reg;
                                                 break;
                                             default:
                                                 assert(0 && "not a v-register!\n");
                                         }
                                     }
                                     break;
                                 case 'i':	// immediate
                                     ss << "0x" << hex << opr.imm;
                                     break;
                                     /*
                                        case 'd':	// data address
                                        ss << "(0x" << hex << opr.mem.disp << ')' << 'v' << dec << opr.mem.reg;
                                        break;
                                        case 'p':	// program counter
                                        ss << ((opr.pc.type == SPC) ? 'S' : 'T')
                                        << "(0x" << hex << opr.pc.disp << ')' << 'v' << opr.pc.reg;
                                        break;
                                        */
                                 case 'c':	// condition code
                                     switch (opr.cc) {
                                         case EQ: ss << "EQ"; break;
                                         case NE: ss << "NE"; break;
                                         case GT: ss << "GT"; break;
                                         case GE: ss << "GE"; break;
                                         case LT: ss << "LT"; break;
                                         case LE: ss << "LE"; break;
                                         case AB: ss << "AB"; break;
                                         case BE: ss << "BE"; break;
                                     }
                                     break;
                                 case 'b':	// block size
                                     switch (opr.size) {
                                         case BYTE:	    ss << "BYTE"; break;
                                         case HALFWORD:  ss << "HALFWORD"; break;
                                         case WORD:	    ss << "WORD"; break;
                                         case DWORD:	    ss << "DWORD"; break;
                                         case FLOAT:	    ss << "FLOAT"; break;
                                         case DOUBLE:    ss << "DOUBLE"; break;
                                     }
                                     break;
                                 case 'w':	// plain word
                                     ss << "0x" << std::hex << opr.word;
                                     break;

                                 case 't':   // data type
                                     switch (type) {
                                         case notype: ss<< "\t"; break;
                                         case b1:    ss << ".b1"; break;
                                         case b8:    ss << ".b8"; break;
                                         case b16:   ss << ".b16"; break;
                                         case b32:   ss << ".b32"; break;
                                         case b64:   ss << ".b64"; break;
                                         case u8:    ss << ".u8"; break;
                                         case u16:   ss << ".u16"; break;
                                         case u32:   ss << ".u32"; break;
                                         case u64:   ss << ".u64"; break;
                                         case s8:    ss << ".s8"; break;
                                         case s16:   ss << ".s16"; break;
                                         case s32:   ss << ".s32"; break;
                                         case s64:   ss << ".s64"; break;
                                         case f16:   ss << ".f16"; break;
                                         case f32:   ss << ".f32"; break;
                                         case f64:   ss << ".f64"; break;
                                     }
                                     break;

                                 case 'y':   // data type
                                     switch (type2) {
                                         case notype: ss<< ""; break;
                                         case b1:    ss << ".b1"; break;
                                         case b8:    ss << ".b8"; break;
                                         case b16:   ss << ".b16"; break;
                                         case b32:   ss << ".b32"; break;
                                         case b64:   ss << ".b64"; break;
                                         case u8:    ss << ".u8"; break;
                                         case u16:   ss << ".u16"; break;
                                         case u32:   ss << ".u32"; break;
                                         case u64:   ss << ".u64"; break;
                                         case s8:    ss << ".s8"; break;
                                         case s16:   ss << ".s16"; break;
                                         case s32:   ss << ".s32"; break;
                                         case s64:   ss << ".s64"; break;
                                         case f16:   ss << ".f16"; break;
                                         case f32:   ss << ".f32"; break;
                                         case f64:   ss << ".f64"; break;
                                     }
                                     break;

                                 default:
                                     ;
                             }
                             format.replace(idx, 2, ss.str());
                             idx++;
                         }

                         return os << format;
        }

}
